package com.carter.springframework.context.event;

import com.carter.springframework.beans.exception.BeanException;
import com.carter.springframework.beans.factory.BeanFactory;
import com.carter.springframework.beans.factory.BeanFactoryAware;
import com.carter.springframework.context.ApplicationEvent;
import com.carter.springframework.context.ApplicationListener;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * 抽象的application事件广播处理
 */
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {

    private final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();

    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeanException {
        this.beanFactory=beanFactory;
    }

    @SuppressWarnings("unchecked")
    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
        applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);
    }

    @Override
    @SuppressWarnings("unchecked")
    public void removeApplicationListener(ApplicationListener<?> listener) {
        applicationListeners.remove(listener);
    }

    /**
     * 获取所有支持此事件的listener
     * @param event
     * @return
     */
    protected Collection<ApplicationListener<ApplicationEvent>> getApplicationListeners(ApplicationEvent event){
        List<ApplicationListener<ApplicationEvent>> supportListeners =  new LinkedList<>();
        for (ApplicationListener<ApplicationEvent> listener:applicationListeners){
            if (supportEvent(listener,event)){
                supportListeners.add(listener);
            }
        }
        return supportListeners;
    }

    /**
     * 判断listener是否支持事件，主要逻辑为拿到Listener泛型中的类型与事件的Class进行比较
     * @param listener
     * @param event
     * @return
     */
    private boolean supportEvent(ApplicationListener<ApplicationEvent> listener, ApplicationEvent event) {
        Class<? extends ApplicationListener> listenerClass = listener.getClass();
        //如果是被cglib动态代理过的，类名会有$$,所以要取superclass才是真正的类名
        Class<?> targetClass = listenerClass.getName().contains("$$") ? listenerClass.getSuperclass() : listenerClass;
        Type genericInterface = targetClass.getGenericInterfaces()[0];
        Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
        String className = actualTypeArgument.getTypeName();
        Class<?> eventClass;
        try {
            eventClass = Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new BeanException("no event class:" + className, e);
        }
        return eventClass.isAssignableFrom(event.getClass());
    }
}
